﻿using MicroCloud.Extensions;

namespace MicroCloud.Extensions
{
    /// <summary>
    /// <see cref="IQueryable{T}"/> 扩展操作类
    /// </summary>
    public static class QueryableExtensions
    {
        /// <summary>
        /// 将 <see cref="IQueryable{T}"/> 集合按指定字段与排序方式进行排序
        /// </summary>
        /// <param name="source">要排序的数据集</param>
        /// <param name="propertyName">排序属性名</param>
        /// <param name="sortDirection">排序方向</param>
        /// <typeparam name="T">动态类型</typeparam>
        /// <returns>排序后的数据集</returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source,
            string propertyName,
            ListSortDirection sortDirection = ListSortDirection.Ascending)
        {
            source.CheckNotNull(nameof(source));
            propertyName.CheckNotNullOrEmpty(nameof(propertyName));

            return CollectionPropertySorter<T>.OrderBy(source, propertyName, sortDirection);
        }

        /// <summary>
        /// 将 <see cref="IQueryable{T}"/> 集合按指定字段排序条件进行排序
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="source">要排序的数据集</param>
        /// <param name="sortCondition">列表字段排序条件</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, SortCondition sortCondition)
        {
            source.CheckNotNull(nameof(source));
            sortCondition.CheckNotNull(nameof(sortCondition));

            return source.OrderBy(sortCondition.SortField, sortCondition.ListSortDirection);
        }

        /// <summary>
        /// 将 <see cref="IQueryable{T}"/> 集合按指定字段排序条件进行排序
        /// </summary>
        /// <typeparam name="T">动态类型</typeparam>
        /// <param name="source">要排序的数据集</param>
        /// <param name="sortCondition">列表字段排序条件</param>
        /// <returns></returns>
        public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, SortCondition<T> sortCondition)
        {
            source.CheckNotNull(nameof(source));
            sortCondition.CheckNotNull(nameof(sortCondition));

            return source.OrderBy(sortCondition.SortField, sortCondition.ListSortDirection);
        }

        #region "方法"
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询指定数据筛选的分页信息"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询指定数据筛选的分页信息
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <typeparam name="TResult">分页数据类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="pageCondition">分页查询条件</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <returns>分页结果信息</returns>
        public static PageResult<TResult> ToPage<TEntity, TResult>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, PageCondition pageCondition, Expression<Func<TEntity, TResult>> selector)
        {
            source.CheckNotNull(nameof(source));
            predicate.CheckNotNull(nameof(predicate));
            pageCondition.CheckNotNull(nameof(pageCondition));
            selector.CheckNotNull(nameof(selector));

            return source.ToPage(predicate, pageCondition.PageIndex, pageCondition.PageSize, pageCondition.SortConditions, selector);
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询指定数据筛选的分页信息"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询指定数据筛选的分页信息
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <typeparam name="TResult">分页数据类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="pageIndex">分页索引</param>
        /// <param name="pageSize">分页大小</param>
        /// <param name="sortConditions">排序条件集合</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <returns>分页结果信息</returns>
        public static PageResult<TResult> ToPage<TEntity, TResult>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, int pageIndex, int pageSize, SortCondition[] sortConditions, Expression<Func<TEntity, TResult>> selector)
        {
            source.CheckNotNull(nameof(source));
            predicate.CheckNotNull(nameof(predicate));
            pageIndex.CheckGreaterThan(nameof(pageIndex), 0);
            pageSize.CheckGreaterThan(nameof(pageSize), 0);
            selector.CheckNotNull(nameof(selector));

            TResult[] data = source.Where(predicate, pageIndex, pageSize, out int total, sortConditions).Select(selector).ToArray();
            return new PageResult<TResult>() { Total = total, Data = data };
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询指定输出DTO的分页信息"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询指定输出DTO的分页信息
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <typeparam name="TOutputDto">输出DTO数据类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="pageCondition">分页查询条件</param>
        /// <returns>分页结果信息</returns>
        public static PageResult<TOutputDto> ToPage<TEntity, TOutputDto>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, PageCondition pageCondition)
            where TOutputDto : IOutputDto
        {
            source.CheckNotNull(nameof(source));
            predicate.CheckNotNull(nameof(predicate));
            pageCondition.CheckNotNull(nameof(pageCondition));
            return source.ToPage<TEntity, TOutputDto>(predicate,
                pageCondition.PageIndex,
                pageCondition.PageSize,
                pageCondition.SortConditions);
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询出指定输出DTO的分页信息"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询出指定输出DTO的分页信息
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <typeparam name="TOutputDto">输出DTO数据类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="pageIndex">分页索引</param>
        /// <param name="pageSize">分页大小</param>
        /// <param name="sortConditions">排序条件集合</param>
        /// <returns>分页结果信息</returns>
        public static PageResult<TOutputDto> ToPage<TEntity, TOutputDto>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, int pageIndex, int pageSize, SortCondition[] sortConditions)
            where TOutputDto : IOutputDto
        {
            source.CheckNotNull(nameof(source));
            predicate.CheckNotNull(nameof(predicate));
            pageIndex.CheckGreaterThan(nameof(pageIndex), 0);
            pageSize.CheckGreaterThan(nameof(pageSize), 0);

            TOutputDto[] data = source.Where(predicate, pageIndex, pageSize, out int total, sortConditions).ToOutput<TEntity, TOutputDto>().ToArray();
            return new PageResult<TOutputDto>() { Total = total, Data = data };
        }
        #endregion
        #region "将数据源映射为指定 <typeparamref name="TOutputDto"/> 的集合，并验证数据的 <see cref="DataAuthOperation.Update"/> , <see cref="DataAuthOperation.Delete"/> 数据权限状态"
        /// <summary>
        /// 将数据源映射为指定 <typeparamref name="TOutputDto"/> 的集合，
        /// 并验证数据的 <see cref="DataAuthOperation.Update"/> , <see cref="DataAuthOperation.Delete"/> 数据权限状态
        /// </summary>
        /// <param name="source">数据源</param>
        /// <param name="getKey">是否应用于获取缓存键时</param>
        public static IQueryable<TOutputDto> ToOutput<TEntity, TOutputDto>(this IQueryable<TEntity> source, bool getKey = false)
        {
            if (!typeof(TOutputDto).IsBaseOn<IDataAuthEnabled>() || getKey)
            {
                return MapperExtensions.ToOutput<TEntity, TOutputDto>(source);
            }

            List<TEntity> entities = source.ToList();
            List<TOutputDto> dtos = new();
            IFilterService filterService = ServiceLocator.Instance.GetService<IFilterService>();
            Func<TEntity, bool> updateFunc = filterService.GetDataFilterExpression<TEntity>(null, DataAuthOperation.Update).Compile();
            Func<TEntity, bool> deleteFunc = filterService.GetDataFilterExpression<TEntity>(null, DataAuthOperation.Delete).Compile();
            foreach (TEntity entity in entities)
            {
                TOutputDto dto = entity.MapTo<TEntity, TOutputDto>();
                IDataAuthEnabled dto2 = (IDataAuthEnabled)dto;
                dto2.Updatable = updateFunc(entity);
                dto2.Deletable = deleteFunc(entity);
                dtos.Add(dto);
            }
            return dtos.AsQueryable();
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询指定分页条件的子数据集"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询指定分页条件的子数据集
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="pageCondition">分页查询条件</param>
        /// <param name="total">输出符合条件的总记录数</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, PageCondition pageCondition, out int total)
        {
            source.CheckNotNull(nameof(source));
            predicate.CheckNotNull(nameof(predicate));
            pageCondition.CheckNotNull(nameof(pageCondition));

            return source.Where(predicate, pageCondition.PageIndex, pageCondition.PageSize, out total, pageCondition.SortConditions);
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询指定分页条件的子数据集"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询指定分页条件的子数据集
        /// </summary>
        /// <typeparam name="TEntity">动态实体类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="pageIndex">分页索引</param>
        /// <param name="pageSize">分页大小</param>
        /// <param name="total">输出符合条件的总记录数</param>
        /// <param name="sortConditions">排序条件集合</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, int pageIndex, int pageSize, out int total, SortCondition[] sortConditions = null)
        {
            source.CheckNotNull(nameof(source));
            predicate.CheckNotNull(nameof(predicate));
            pageIndex.CheckGreaterThan(nameof(pageIndex), 0);
            pageSize.CheckGreaterThan(nameof(pageSize), 0);

            total = source.Count(predicate);
            source = source.Where(predicate);
            source = source.Sort(sortConditions);

            return source != null ? source.Skip((pageIndex - 1) * pageSize).Take(pageSize) : Enumerable.Empty<TEntity>().AsQueryable();
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询指定排序条件的子数据集"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询指定排序条件的子数据集
        /// </summary>
        /// <typeparam name="TEntity">动态实体类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="sortCondition">排序条件</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, SortCondition sortCondition)
        {
            source.CheckNotNull(nameof(source));
            predicate.CheckNotNull(nameof(predicate));

            source = source.Where(predicate, [sortCondition]);
            return source;
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询指定排序条件的子数据集"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询指定排序条件的子数据集
        /// </summary>
        /// <typeparam name="TEntity">动态实体类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="sortConditions">排序条件集合</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, SortCondition[] sortConditions)
        {
            source.CheckNotNull(nameof(source));
            predicate.CheckNotNull(nameof(predicate));

            source = source.Where(predicate);
            //source = Queryable.Where(source, predicate);
            source = source.Sort(sortConditions);
            return source ?? Enumerable.Empty<TEntity>().AsQueryable();
        }
        #endregion
        #region "设定指定 <see cref="IQueryable{T}"/> 集合的排序条件"
        /// <summary>
        /// 设定指定 <see cref="IQueryable{T}"/> 集合的排序条件
        /// </summary>
        /// <typeparam name="TEntity">动态实体类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="sortConditions">排序条件集合</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Sort<TEntity>(this IQueryable<TEntity> source, params SortCondition[] sortConditions)
        {
            source.CheckNotNull(nameof(source));

            if (sortConditions == null || sortConditions.Length == 0)
            {
                if (typeof(TEntity).IsEntityType())
                {
                    source = source.OrderBy("Id");
                }
                else if (typeof(TEntity).IsBaseOn<ICreatedTime>())
                {
                    source = source.OrderBy("CreatedTime");
                }
                else
                {
                    //throw new EaCloudException(I18N.T("{0} 未添加默认排序方式", typeof(TEntity).GetDescription()));
                    return source ?? Enumerable.Empty<TEntity>().AsQueryable();
                }
            }
            else
            {
                int count = 0;
                IOrderedQueryable<TEntity> orderSource = null;
                foreach (SortCondition sortCondition in sortConditions)
                {
                    orderSource = count == 0
                        ? CollectionPropertySorter<TEntity>.OrderBy(source, sortCondition.SortField, sortCondition.ListSortDirection)
                        : CollectionPropertySorter<TEntity>.ThenBy(orderSource, sortCondition.SortField, sortCondition.ListSortDirection);
                    count++;
                }
                source = orderSource;
            }
            return source ?? Enumerable.Empty<TEntity>().AsQueryable();
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询未过期的子数据集，用于筛选实现了 <see cref="IExpirable"/> 接口的数据集"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询未过期的子数据集，用于筛选实现了 <see cref="IExpirable"/> 接口的数据集
        /// </summary>
        /// <typeparam name="TEntity">数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Unexpired<TEntity>(this IQueryable<TEntity> source)
            where TEntity : class, IExpirable
        {
            DateTime now = DateTime.Now;
            Expression<Func<TEntity, bool>> predicate =
                m => (m.BeginTime == null || m.BeginTime.Value <= now) && (m.EndTime == null || m.EndTime.Value >= now);
            return source.Where(predicate);
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 集合中查询已过期的子数据集，用于筛选实现了 <see cref="IExpirable"/> 接口的数据集"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 集合中查询已过期的子数据集，用于筛选实现了 <see cref="IExpirable"/> 接口的数据集
        /// </summary>
        /// <typeparam name="TEntity">数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Expired<TEntity>(this IQueryable<TEntity> source)
            where TEntity : class, IExpirable
        {
            DateTime now = DateTime.Now;
            Expression<Func<TEntity, bool>> predicate = m => m.EndTime != null && m.EndTime.Value < now;
            return source.Where(predicate);
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 数据集中查询未锁定的子数据集，用于筛选实现了 <see cref="ILockable"/> 接口的数据集"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 数据集中查询未锁定的子数据集，用于筛选实现了 <see cref="ILockable"/> 接口的数据集
        /// </summary>
        /// <typeparam name="TEntity">数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Unlocked<TEntity>(this IQueryable<TEntity> source)
            where TEntity : class, ILockable
        {
            return source.Where(m => !m.IsLocked);
        }
        #endregion
        #region "从指定 <see cref="IQueryable{T}"/> 数据集中查询已锁定的子数据集，用于筛选实现了 <see cref="ILockable"/> 接口的数据集"
        /// <summary>
        /// 从指定 <see cref="IQueryable{T}"/> 数据集中查询已锁定的子数据集，用于筛选实现了 <see cref="ILockable"/> 接口的数据集
        /// </summary>
        /// <typeparam name="TEntity">数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <returns></returns>
        public static IQueryable<TEntity> Locked<TEntity>(this IQueryable<TEntity> source)
            where TEntity : class, ILockable
        {
            return source.Where(m => m.IsLocked);
        }
        #endregion

        #region "查询分页数据结果，如缓存存在，直接返回，否则从数据源查找分页结果，并存入缓存中再返回"
        /// <summary>
        /// 查询分页数据结果，如缓存存在，直接返回，否则从数据源查找分页结果，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TEntity">数据源的项数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="pageCondition">数据分页条件</param>
        /// <param name="selector">数据选择表达式</param>
        /// <param name="cacheSeconds">缓存秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static PageResult<TResult> ToPageCache<TEntity, TResult>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, PageCondition pageCondition, Expression<Func<TEntity, TResult>> selector,
            int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source, predicate, pageCondition, selector, keyParams);
            return cache.Get(key, () => source.ToPage(predicate, pageCondition, selector), cacheSeconds, absolute);
        }
        #endregion
        #region "查询分页数据结果，如缓存存在，直接返回，否则从数据源查找分页结果，并存入缓存中再返回"
        /// <summary>
        /// 查询分页数据结果，如缓存存在，直接返回，否则从数据源查找分页结果，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TEntity">数据源的项数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="pageCondition">数据分页条件</param>
        /// <param name="selector">数据选择表达式</param>
        /// <param name="function">功能对象</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static PageResult<TResult> ToPageCache<TEntity, TResult>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, PageCondition pageCondition, Expression<Func<TEntity, TResult>> selector,
            IFunction function, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source, predicate, pageCondition, selector, keyParams);
            return cache.Get(key, () => source.ToPage(predicate, pageCondition, selector), function);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <param name="cacheSeconds">缓存时间：秒</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static List<TResult> ToCacheList<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, Expression<Func<TSource, TResult>> selector,
            int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            return source.Where(predicate).ToCacheList(selector, cacheSeconds, absolute, keyParams);
        }
        #endregion
        #region "将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <param name="cacheSeconds">缓存时间：秒</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static TResult[] ToCacheArray<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, Expression<Func<TSource, TResult>> selector,
            int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            return source.Where(predicate).ToCacheArray(selector, cacheSeconds, absolute, keyParams);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static List<TResult> ToCacheList<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, Expression<Func<TSource, TResult>> selector,
            IFunction function, params object[] keyParams)
        {
            return source.Where(predicate).ToCacheList(selector, function, keyParams);
        }
        #endregion
        #region "将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static TResult[] ToCacheArray<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, Expression<Func<TSource, TResult>> selector,
            IFunction function, params object[] keyParams)
        {
            return source.Where(predicate).ToCacheArray(selector, function, keyParams);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <param name="cacheSeconds">缓存的秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static List<TResult> ToCacheList<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector,
            int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source, selector, keyParams);
            return cache.Get(key, () => source.Select(selector).ToList(), cacheSeconds, absolute);
        }
        #endregion
        #region "将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <param name="cacheSeconds">缓存的秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static TResult[] ToCacheArray<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector,
            int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source, selector, keyParams);
            return cache.Get(key, () => source.Select(selector).ToArray(), cacheSeconds, absolute);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static List<TResult> ToCacheList<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector,
            IFunction function, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source, selector, keyParams);
            return cache.Get(key, () => source.Select(selector).ToList(), function);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TResult">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="selector">数据筛选表达式</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static TResult[] ToCacheArray<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector,
            IFunction function, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source, selector, keyParams);
            return cache.Get(key, () => source.Select(selector).ToArray(), function);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="cacheSeconds">缓存的秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static List<TSource> ToCacheList<TSource>(this IQueryable<TSource> source, int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source.Expression, keyParams);
            return cache.Get(key, source.ToList, cacheSeconds, absolute);
        }
        #endregion
        #region "将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="cacheSeconds">缓存的秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static TSource[] ToCacheArray<TSource>(this IQueryable<TSource> source, int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source.Expression, keyParams);
            return cache.Get(key, source.ToArray, cacheSeconds, absolute);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static List<TSource> ToCacheList<TSource>(this IQueryable<TSource> source, IFunction function, params object[] keyParams)
        {
            if (function == null || function.CacheExpirationSeconds <= 0)
            {
                return source.ToList();
            }
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source.Expression, keyParams);
            return cache.Get(key, source.ToList, function);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static TSource[] ToCacheArray<TSource>(this IQueryable<TSource> source, IFunction function, params object[] keyParams)
        {
            if (function == null || function.CacheExpirationSeconds <= 0)
            {
                return source.ToArray();
            }
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source.Expression, keyParams);
            return cache.Get(key, source.ToArray, function);
        }
        #endregion
        //OutputDto
        #region "查询分页数据结果，如缓存存在，直接返回，否则从数据源查找分页结果，并存入缓存中再返回"
        /// <summary>
        /// 查询分页数据结果，如缓存存在，直接返回，否则从数据源查找分页结果，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <typeparam name="TOutputDto">分页数据类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="pageCondition">分页查询条件</param>
        /// <param name="cacheSeconds">缓存的秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询的分页结果</returns>
        public static PageResult<TOutputDto> ToPageCache<TEntity, TOutputDto>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, PageCondition pageCondition
            , int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
            where TOutputDto : IOutputDto
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey<TEntity, TOutputDto>(source, predicate, pageCondition, keyParams);
            return cache.Get(key, () => source.ToPage<TEntity, TOutputDto>(predicate, pageCondition), cacheSeconds, absolute);
        }
        #endregion
        #region "查询分页数据结果，如缓存存在，直接返回，否则从数据源查找分页结果，并存入缓存中再返回"
        /// <summary>
        /// 查询分页数据结果，如缓存存在，直接返回，否则从数据源查找分页结果，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TEntity">实体类型</typeparam>
        /// <typeparam name="TOutputDto">分页数据类型</typeparam>
        /// <param name="source">要查询的数据集</param>
        /// <param name="predicate">查询条件谓语表达式</param>
        /// <param name="pageCondition">分页查询条件</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询的分页结果</returns>
        public static PageResult<TOutputDto> ToPageCache<TEntity, TOutputDto>(this IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, PageCondition pageCondition, IFunction function, params object[] keyParams)
            where TOutputDto : IOutputDto
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey<TEntity, TOutputDto>(source, predicate, pageCondition, keyParams);
            return cache.Get(key, () => source.ToPage<TEntity, TOutputDto>(predicate, pageCondition), function);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="cacheSeconds">缓存时间：秒</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static List<TOutputDto> ToCacheList<TSource, TOutputDto>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            return source.Where(predicate).ToCacheList<TSource, TOutputDto>(cacheSeconds, absolute, keyParams);
        }
        #endregion
        #region "将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="cacheSeconds">缓存时间：秒</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static TOutputDto[] ToCacheArray<TSource, TOutputDto>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            return source.Where(predicate).ToCacheArray<TSource, TOutputDto>(cacheSeconds, absolute, keyParams);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static List<TOutputDto> ToCacheList<TSource, TOutputDto>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, IFunction function, params object[] keyParams)
        {
            return source.Where(predicate).ToCacheList<TSource, TOutputDto>(function, keyParams);
        }
        #endregion
        #region "将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns></returns>
        public static TOutputDto[] ToCacheArray<TSource, TOutputDto>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate, IFunction function, params object[] keyParams)
        {
            return source.Where(predicate).ToCacheArray<TSource, TOutputDto>(function, keyParams);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="cacheSeconds">缓存的秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static List<TOutputDto> ToCacheList<TSource, TOutputDto>(this IQueryable<TSource> source, int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey<TSource, TOutputDto>(source, keyParams);
            return cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().ToList(), cacheSeconds, absolute);
        }
        #endregion
        #region "将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="cacheSeconds">缓存的秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static TOutputDto[] ToCacheArray<TSource, TOutputDto>(this IQueryable<TSource> source, int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey<TSource, TOutputDto>(source, keyParams);
            return cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().ToArray(), cacheSeconds, absolute);
        }
        #endregion
        #region "将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的列表，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static List<TOutputDto> ToCacheList<TSource, TOutputDto>(this IQueryable<TSource> source, IFunction function, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey<TSource, TOutputDto>(source, keyParams);
            return cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().ToList(), function);
        }
        #endregion
        #region "将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回"
        /// <summary>
        /// 将结果转换为缓存的数组，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>查询结果</returns>
        public static TOutputDto[] ToCacheArray<TSource, TOutputDto>(this IQueryable<TSource> source, IFunction function, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey<TSource, TOutputDto>(source, keyParams);
            return cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().ToArray(), function);
        }
        #endregion
        #region "将第一个符合条件的数据转换为缓存的结果，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回。"
        /// <summary>
        /// 将第一个符合条件的数据转换为缓存的结果，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回。
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="cacheSeconds">缓存的秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>符合条件的结果</returns>
        public static TOutputDto ToCacheFirst<TSource, TOutputDto>(this IQueryable<TSource> source, int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey<TSource, TOutputDto>(source, keyParams);
            return cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().FirstOrDefault(), cacheSeconds, absolute);
        }
        #endregion
        #region "将第一个符合条件的数据转换为缓存的结果，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回。"
        /// <summary>
        /// 将第一个符合条件的数据转换为缓存的结果，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回。
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>符合条件的结果</returns>
        public static TOutputDto ToCacheFirst<TSource, TOutputDto>(this IQueryable<TSource> source, IFunction function, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey<TSource, TOutputDto>(source, keyParams);
            return cache.Get(key, () => source.ToOutput<TSource, TOutputDto>().FirstOrDefault(), function);
        }
        #endregion

        #region "将结果转换为数组"
        /// <summary>
        /// 将结果转换为数组
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <returns></returns>
        public static TOutputDto[] ToArray<TSource, TOutputDto>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return source.Where(predicate).ToOutput<TSource, TOutputDto>().ToArray();
        }
        #endregion
        #region "将结果转换为数组"
        /// <summary>
        /// 将结果转换为数组
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <returns></returns>
        public static TOutputDto[] ToArray<TSource, TOutputDto>(this IQueryable<TSource> source)
        {
            return source.ToOutput<TSource, TOutputDto>().ToArray();
        }
        #endregion
        #region "将结果转换为列表"
        /// <summary>
        /// 将结果转换为列表
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <returns></returns>
        public static List<TOutputDto> ToList<TSource, TOutputDto>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return source.Where(predicate).ToOutput<TSource, TOutputDto>().ToList();
        }
        #endregion
        #region "将结果转换为列表"
        /// <summary>
        /// 将结果转换为列表
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <returns></returns>
        public static List<TOutputDto> ToList<TSource, TOutputDto>(this IQueryable<TSource> source)
        {
            return source.ToOutput<TSource, TOutputDto>().ToList();
        }
        #endregion
        #region "将第一个符合条件的数据转换为指定类型结果"
        /// <summary>
        /// 将第一个符合条件的数据转换为指定类型结果
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <param name="predicate">数据查询表达式</param>
        /// <returns></returns>
        public static TOutputDto ToFirst<TSource, TOutputDto>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
        {
            return source.Where(predicate).ToOutput<TSource, TOutputDto>().FirstOrDefault();
        }
        #endregion
        #region "将第一个符合条件的数据转换为指定类型结果"
        /// <summary>
        /// 将第一个符合条件的数据转换为指定类型结果
        /// </summary>
        /// <typeparam name="TSource">数据源的项数据类型</typeparam>
        /// <typeparam name="TOutputDto">结果集的项数据类型</typeparam>
        /// <param name="source">数据源</param>
        /// <returns></returns>
        public static TOutputDto ToFirst<TSource, TOutputDto>(this IQueryable<TSource> source)
        {
            return source.ToOutput<TSource, TOutputDto>().FirstOrDefault();
        }
        #endregion

        //Entity
        #region "将第一个符合条件的数据转换为缓存的结果，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回。"
        /// <summary>
        /// 将第一个符合条件的数据转换为缓存的结果，如缓存存在，直接返回，否则从数据源查询，并存入缓存中再返回。
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="cacheSeconds">缓存的秒数</param>
        /// <param name="absolute">是否绝对过期</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>符合条件的结果</returns>
        public static TSource ToCacheFirst<TSource>(this IQueryable<TSource> source, int cacheSeconds = 60, bool absolute = true, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source.Expression, keyParams);
            return cache.Get(key, () => source.FirstOrDefault(), cacheSeconds, absolute);
        }
        #endregion
        #region "将第一个符合条件的数据转换为缓存的结果，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回。"
        /// <summary>
        /// 将第一个符合条件的数据转换为缓存的结果，如缓存存在，直接返回，否则从数据源查询，并按指定缓存策略存入缓存中再返回。
        /// </summary>
        /// <typeparam name="TSource">源数据类型</typeparam>
        /// <param name="source">查询数据源</param>
        /// <param name="function">缓存策略相关功能</param>
        /// <param name="keyParams">缓存键参数</param>
        /// <returns>符合条件的结果</returns>
        public static TSource ToCacheFirst<TSource>(this IQueryable<TSource> source,
            IFunction function, params object[] keyParams)
        {
            IDistributedCache cache = ServiceLocator.Instance.GetService<IDistributedCache>();
            string key = GetKey(source.Expression, keyParams);
            return cache.Get(key, () => source.FirstOrDefault(), function);
        }
        #endregion

        #endregion

        #region "私有方法"
        #region "获取密钥"
        /// <summary>
        /// 获取密钥
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="source"></param>
        /// <param name="predicate"></param>
        /// <param name="pageCondition"></param>
        /// <param name="selector"></param>
        /// <param name="keyParams"></param>
        /// <returns></returns>
        private static string GetKey<TEntity, TResult>(IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, PageCondition pageCondition, Expression<Func<TEntity, TResult>> selector, params object[] keyParams)
        {
            source = source.Where(predicate);
            SortCondition[] sortConditions = pageCondition.SortConditions;
            if (sortConditions == null || sortConditions.Length == 0)
            {
                if (typeof(TEntity).IsEntityType())
                {
                    source = source.OrderBy("Id");
                }
                else if (typeof(TEntity).IsBaseOn<ICreatedTime>())
                {
                    source = source.OrderBy("CreatedTime");
                }
                else
                {
                    throw new Exception(I18N.T("{0} 未添加默认排序方式", typeof(TEntity).GetDescription()));
                }
            }
            else
            {
                int count = 0;
                IOrderedQueryable<TEntity> orderSource = null;
                foreach (SortCondition sortCondition in sortConditions)
                {
                    orderSource = count == 0
                        ? CollectionPropertySorter<TEntity>.OrderBy(source, sortCondition.SortField, sortCondition.ListSortDirection)
                        : CollectionPropertySorter<TEntity>.ThenBy(orderSource, sortCondition.SortField, sortCondition.ListSortDirection);
                    count++;
                }
                source = orderSource;
            }
            int pageIndex = pageCondition.PageIndex, pageSize = pageCondition.PageSize;
            source = source != null
                ? source.Skip((pageIndex - 1) * pageSize).Take(pageSize)
                : Enumerable.Empty<TEntity>().AsQueryable();
            IQueryable<TResult> query = source.Select(selector);
            return GetKey(query.Expression, keyParams);
        }
        #endregion
        #region "获取密钥"
        /// <summary>
        /// 获取密钥
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="TOutputDto"></typeparam>
        /// <param name="source"></param>
        /// <param name="predicate"></param>
        /// <param name="pageCondition"></param>
        /// <param name="keyParams"></param>
        /// <returns></returns>
        private static string GetKey<TEntity, TOutputDto>(IQueryable<TEntity> source, Expression<Func<TEntity, bool>> predicate, PageCondition pageCondition, params object[] keyParams)
            where TOutputDto : IOutputDto
        {
            source = source.Where(predicate);
            SortCondition[] sortConditions = pageCondition.SortConditions;
            if (sortConditions == null || sortConditions.Length == 0)
            {
                if (typeof(TEntity).IsEntityType())
                {
                    source = source.OrderBy("Id");
                }
                else if (typeof(TEntity).IsBaseOn<ICreatedTime>())
                {
                    source = source.OrderBy("CreatedTime");
                }
                else
                {
                    throw new Exception(I18N.T("{0} 未添加默认排序方式", typeof(TEntity).GetDescription()));
                }
            }
            else
            {
                int count = 0;
                IOrderedQueryable<TEntity> orderSource = null;
                foreach (SortCondition sortCondition in sortConditions)
                {
                    orderSource = count == 0
                        ? CollectionPropertySorter<TEntity>.OrderBy(source, sortCondition.SortField, sortCondition.ListSortDirection)
                        : CollectionPropertySorter<TEntity>.ThenBy(orderSource, sortCondition.SortField, sortCondition.ListSortDirection);
                    count++;
                }
                source = orderSource;
            }
            int pageIndex = pageCondition.PageIndex, pageSize = pageCondition.PageSize;
            source = source != null
                ? source.Skip((pageIndex - 1) * pageSize).Take(pageSize)
                : Enumerable.Empty<TEntity>().AsQueryable();
            IQueryable<TOutputDto> query = source.ToOutput<TEntity, TOutputDto>(true);
            return GetKey(query.Expression, keyParams);
        }
        #endregion
        #region "获取密钥"
        /// <summary>
        /// 获取密钥
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TOutputDto"></typeparam>
        /// <param name="source"></param>
        /// <param name="keyParams"></param>
        /// <returns></returns>
        private static string GetKey<TSource, TOutputDto>(IQueryable<TSource> source, params object[] keyParams)
        {
            IQueryable<TOutputDto> query = source.ToOutput<TSource, TOutputDto>();
            return GetKey(query.Expression, keyParams);
        }
        #endregion
        #region "获取密钥"
        /// <summary>
        /// 获取密钥
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="source"></param>
        /// <param name="selector"></param>
        /// <param name="keyParams"></param>
        /// <returns></returns>
        private static string GetKey<TSource, TResult>(IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector, params object[] keyParams)
        {
            IQueryable<TResult> query = source.Select(selector);
            return GetKey(query.Expression, keyParams);
        }
        #endregion
        #region "获取密钥"
        /// <summary>
        /// 获取密钥
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="keyParams"></param>
        /// <returns></returns>
        private static string GetKey(Expression expression, params object[] keyParams)
        {
            string key;
            try
            {
                key = new ExpressionCacheKeyGenerator(expression).GetKey(keyParams);
            }
            catch (TargetInvocationException)
            {
                key = new StringCacheKeyGenerator().GetKey(keyParams);
            }
            return key.ToMD5();
        }
        #endregion

        #endregion

    }

}
